﻿using System;
using System.Collections;
using System.Collections.Generic;
using DimensionsHelper.Common.Contracts.Metadata;
using System.Diagnostics.CodeAnalysis;

namespace DimensionsHelper.Services.Metadata;

public class DataSource : MetadataNamedObject, IDataSource
{
    internal DataSource(IMetadataDocument document, string name)
        : base(document, ObjectType.DataSource, name)
    {
        DbLocation = string.Empty;
        Cdsc = string.Empty;
        Project = string.Empty;
        Id = string.Empty;
        AliasMap = new AliasMap(document);
    }

    public string DbLocation { get; internal set; }

    public string Cdsc { get; internal set; }

    public string Project { get; internal set; }

    public string Id { get; internal set; }

    public IAliasMap AliasMap { get; internal set; }
}

public class DataSources : MetadataStringKeyCollection<IDataSource>, IDataSources
{
    public DataSources(IMetadataDocument document)
    {
        ObjectType = ObjectType.DataSources;
        Document = document;
        DefaultName = string.Empty;
        Properties = new Properties(document, this);
        Versioned = true;
        Parent = document;
    }

    public IProperties Properties { get; }

    public string DefaultName { get; internal set; }

    public IDataSource Default
    {
        get
        {
            if (!TryGetByKey(DefaultName, out var obj))
            {
                throw new Exception($"未找到名为'{DefaultName}'，类型是'{nameof(IDataSource)}'的配置对象。");
            }

            return obj;
        }
    }

    public IMetadataDocument Document { get; }

    public ObjectType ObjectType { get; }

    public bool Versioned { get; set; }

    public override IDataSource CreateObject(string name)
    {
        return new DataSource(Document, name);
    }

    public IMetadataObject Parent { get; }

    public IVersionObjectCollection? Versions { get; internal set; }

    public T? FindParent<T>() where T : IMetadataObject
    {
        return MetadataObject.FindParent<T>(this);
    }

    protected override string KeySelector(IDataSource item)
    {
        return item.Name;
    }
}

public class Alias : IAlias
{
    private readonly Dictionary<string, NativeValue> _nativeNameMap;
    private readonly Dictionary<string, NativeValue> _nativeValueMap;

    private readonly List<NativeValue> _nativeValues;

    private readonly List<SubAlias> _subAliases;
    private readonly Dictionary<int, SubAlias> _subAliasIndexMap;
    private readonly Dictionary<string, SubAlias> _subAliasNameMap;

    internal Alias(IMetadataDocument document, string name, string aliasName)
    {
        _subAliases = [];
        _subAliasIndexMap = new Dictionary<int, SubAlias>();
        _subAliasNameMap = new Dictionary<string, SubAlias>();

        _nativeValues = [];
        _nativeNameMap = new Dictionary<string, NativeValue>();
        _nativeValueMap = new Dictionary<string, NativeValue>();

        Properties = new Properties(document);
        FullName = name;
        AliasName = aliasName;
    }

    public SubAlias this[int index] => _subAliases[index];

    public IProperties Properties { get; }

    public IReadOnlyList<NativeValue> NativeValues => _nativeValues;

    public string FullName { get; }

    public string AliasName { get; internal set; }

    public bool AddSubAlias(int index, string name)
    {
        if (index < 0 || string.IsNullOrEmpty(name) ||
            _subAliasIndexMap.ContainsKey(index) || _subAliasNameMap.ContainsKey(name.ToLower()))
        {
            return false;
        }

        var alias = new SubAlias(index, name);

        _subAliases.Add(alias);
        _subAliasIndexMap.Add(index, alias);
        _subAliasNameMap.Add(name.ToLower(), alias);

        return true;
    }


    public bool TryGetSubAliasByName(string name, [MaybeNullWhen(false)] out SubAlias subAlias)
    {
        return _subAliasNameMap.TryGetValue(name.ToLower(), out subAlias);
    }


    public bool TryGetSubAliasByIndex(int index, [MaybeNullWhen(false)] out SubAlias subAlias)
    {
        return _subAliasIndexMap.TryGetValue(index, out subAlias);
    }


    public bool AddNativeValue(string fullName, string value)
    {
        if (string.IsNullOrEmpty(fullName) || string.IsNullOrEmpty(value) ||
            _nativeNameMap.ContainsKey(fullName.ToLower()) || _nativeValueMap.ContainsKey(value.ToLower()))
        {
            return false;
        }

        var item = new NativeValue(fullName, value);

        _nativeValues.Add(item);
        _nativeNameMap.Add(fullName.ToLower(), item);
        _nativeValueMap.Add(value.ToLower(), item);

        return true;
    }

    public bool TryGetNativeValueByName(string fullName, [MaybeNullWhen(false)] out NativeValue nativeValue)
    {
        return _nativeNameMap.TryGetValue(fullName.ToLower(), out nativeValue);
    }


    public bool TryGetNativeValueByValue(string value, [MaybeNullWhen(false)] out NativeValue nativeValue)
    {
        return _nativeValueMap.TryGetValue(value.ToLower(), out nativeValue);
    }


    #region IEnumerable

    public IEnumerator<SubAlias> GetEnumerator()
    {
        return _subAliases.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    #endregion
}

public class AliasMap : MetadataObject, IAliasMap
{
    private readonly Dictionary<string, IAlias> _nameMap;

    internal AliasMap(IMetadataDocument document) : base(document, ObjectType.AliasMap)
    {
        _nameMap = new Dictionary<string, IAlias>();
    }

    public IAlias AddNew(string fullName, string aliasName)
    {
        var lowerName = fullName.ToLower();

        if (_nameMap.ContainsKey(lowerName))
        {
            throw new Exception($"名为'{fullName}'变量的别名已存在。");
        }

        var alias = new Alias(Document, fullName, aliasName);
        _nameMap.Add(lowerName, alias);
        return alias;
    }

    public int Count => _nameMap.Count;

    public IEnumerator<IAlias> GetEnumerator()
    {
        return _nameMap.Values.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public IAlias? Get(string fullName)
    {
        return _nameMap.GetValueOrDefault(fullName);
    }

    public bool TryGet(string fullName, [MaybeNullWhen(false)] out IAlias alias)
    {
        return _nameMap.TryGetValue(fullName, out alias);
    }
}